import time

import ubluetooth
from machine import Pin, Timer
from umqtt.robust import MQTTClient

_LED_PIN = 2

# 蓝牙协议常量定义
_HR_UUID = ubluetooth.UUID(0x180D)  # 心率服务UUID
_HR_MEASUREMENT_UUID = ubluetooth.UUID(0x2A37)  # 心率测量特征
_DEVICE_NAME = "心率模拟器01"  # 设备名称
# 蓝牙服务配置
_HR_SERVICE = (
    _HR_UUID,
    (
        (_HR_MEASUREMENT_UUID, ubluetooth.FLAG_READ | ubluetooth.FLAG_NOTIFY),
    )
)


def build_device_name_ad(device_name):
    # 编码设备名称
    encoded_name = device_name.encode("utf-8")
    # 计算 Length
    ad_type = 0x09  # Complete Local Name
    ad_length = len(encoded_name) + 1
    # 创建 AD Structure
    ad_structure = bytearray([ad_length, ad_type]) + encoded_name
    return ad_structure


# 心率数据格式配置
class HeartRateService:
    def __init__(self):
        self._led = Pin(_LED_PIN, Pin.OUT)
        self._timer0 = Timer(0)
        self._timer1 = Timer(1)
        self._ble = ubluetooth.BLE()
        self._ble.active(True)
        self._ble.config(gap_name=_DEVICE_NAME)

        # 注册服务
        ((self._hr_measurement,),) = self._ble.gatts_register_services((_HR_SERVICE,))

        # 连接状态跟踪
        self._connections = set()
        self._payload = (b'\x02\x01\x06'
                         + build_device_name_ad(_DEVICE_NAME) +
                         b'\x03\x03\x0D\x18')

        # 设置BLE回调
        self._ble.irq(self._irq)

    def blink(self, timer):
        self._led.value(not self._led.value())

    def _irq(self, event, data):
        # 蓝牙连接事件
        if event == 1:
            self._timer1.deinit()
            self._timer0.init(period=800, mode=Timer.PERIODIC, callback=self.blink)
            conn_handle, _, _ = data
            self._connections.add(conn_handle)
            print("主机蓝牙已连接", conn_handle)
        # 蓝牙断开事件
        elif event == 2:
            self._timer0.deinit()
            self._timer1.init(period=100, mode=Timer.PERIODIC, callback=self.blink)
            conn_handle, _, _ = data
            self._connections.remove(conn_handle)
            print("主机蓝牙已断开", conn_handle)
            # 重新开始广播
            self.advertise()

    def advertise(self, interval_us=500000):
        self._ble.gap_advertise(interval_us, adv_data=self._payload)
        print("从机蓝牙广播中...")
        self._timer1.init(period=100, mode=Timer.PERIODIC, callback=self.blink)

    def update_heart_rate(self, value):
        # 数据打包 (flags + heart rate value)
        data = bytearray([0x00, value])  # 0x00表示UINT8格式
        self._ble.gatts_write(self._hr_measurement, data)
        # 向所有连接设备发送通知
        for conn_handle in self._connections:
            self._ble.gatts_notify(conn_handle, self._hr_measurement)
        print("当前心率:", value, "BPM")


MQTT_CLIENT_ID = b"esp32_hr"
MQTT_BROKER = "180.107.109.102"
MQTT_PORT = 1883
MQTT_USER = "admin"
MQTT_PASSWORD = "nttieren0513"
MQTT_TOPIC = b"hr"


class MqttClient:
    def __init__(self):
        self._current_hr = 80
        self._client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER, port=MQTT_PORT, user=MQTT_USER, password=MQTT_PASSWORD,
                                  keepalive=20)

        self._client.DELAY = 1
        self._client.DEBUG = True

        self._client.set_callback(self._mqtt_callback)
        if not self._client.connect(clean_session=False):
            print("MQTT 连接成功")
            self._client.subscribe(topic=MQTT_TOPIC, qos=1)
            print("MQTT 订阅成功")

        self._timer = Timer(3)
        self._timer.init(period=500, mode=Timer.PERIODIC, callback=self.check_msg)

    def _mqtt_callback(self, topic, msg):
        print("MQTT消息:", topic, msg.decode())
        if topic == MQTT_TOPIC:
            try:
                self._current_hr = int(msg.decode())
            except ValueError:
                print("无效的心率值", msg.decode())

    def check_msg(self, timer):
        self._client.check_msg()  # 非阻塞检查

    def heart_rate(self):
        return self._current_hr


def loop_hr():
    # 建立MQTT客户端
    mqtt_client = MqttClient()
    # 运行主程序
    hr = HeartRateService()
    # 发起广播
    hr.advertise()
    # 主循环
    try:
        while True:
            heart_rate = mqtt_client.heart_rate()
            hr.update_heart_rate(heart_rate)
            time.sleep(1)
    except KeyboardInterrupt:
        print("服务停止")
